<?php


namespace app\common\controller;

use custom\Dense;
use custom\Err;
use think\App;
use think\Exception;
use think\Controller;
use app\common\lib\RespCode;
use think\facade\Env;
use think\facade\Request;


class BaseController extends Controller {
    /**
     * 是否加载语言包
     * @var bool
     */
    protected $isLanguage = false;

    /**
     * 是否需要权鉴
     * @var bool
     */
    protected $isAuth = true;

    /**
     * 不进行token验证的方法，*表示所有
     * @var bool
     */
    protected $notNeedToken = [];

    /**
     * 不需要手机号的方法
     * @var array
     */
    protected $notNeedPhone = [];

    /**
     * 需要权限验证的方法，*表示所有
     * @var array
     */
    protected $access = [];

    /**
     * 是否签名验证
     * @var bool
     */
    protected $isSign = false;

    /**
     * 是否加密解密
     * @var bool
     */
    protected $isEncrypt = false;

    /**
     * header头必传参数验证
     * @var array
     */
    protected $headersFields = [];

    /**
     * 接口参数
     * @var
     */
    protected $params;

    /**
     * header头数据
     * @var
     */
    protected $headers;

    /**
     * 登錄用戶信息
     * @var array
     */
    protected $uInfo = [];

    /**
     * 防止重复提交的方法
     * @var array
     */
    protected $resubmitLock = [];

    /**
     * 正确返回结果是数组的方法
     * @var array
     */
    protected $isArrayResult = [];

    /**
     * ios默认加密key
     * @var string
     */
    protected $ios_key = 'ios(%#*&h2k3s4d-';
    /**
     * 安卓默认加密key
     * @var string
     */
    protected $and_key = 'and(&$@*f5s7k9u+';

    public function initialize(App $app = null) {
        parent::initialize($app);
        $this->params = $this->request->param();
        $this->headersFields && $this->checkHeaders();
        $this->isAuth && $this->auth();
        $this->isSign && $this->checkSign($this->params);
        $this->isEncrypt && $this->decryptData($this->params['datas']);

    }

    /**
     * 权鉴
     */
    protected function auth() {

    }

    /**
     * headers头数据验证
     */
    public function checkHeaders() {
        $headers = [];
        foreach ($_SERVER as $name => $value) {
            if (substr($name, 0, 5) == 'HTTP_') {
                $name = strtolower(substr($name, 5));
                $headers[$name] = $value;
            }
        }
        foreach ($this->headersFields as $h) {

            if (!isset($headers[$h])) {
                return $this->resErr("$h can not be empty", RespCode::HEADER_ERR);
            }
            $isTrue = $headers[$h] !== 0 && $headers[$h] !== '0';
            if (isset($headers[$h]) && empty($headers[$h]) && $isTrue) {
                return $this->resErr("$h can not be empty", RespCode::HEADER_ERR);
            }

            $this->headers[$h] = $headers[$h];
        }
    }

    /**
     * 验证签名
     * @param $data 接口参数
     */
    protected function checkSign($data) {
        // 验证签名是否正确
        if ($data['sign'] != md5($data['datas'] . $data['timestamp'])) {
            return $this->resErr('签名错误', RespCode::SIGN_ERR);
        }
        // 是否过期,过期时间150秒。
        if (time() > $data['timestamp'] + 150) {
            return $this->resErr('签名过期', RespCode::SIGN_TIME_OUT);
        }
    }

    /**
     * 解密
     * @param $encryptData 密文
     */
    protected function decryptData($encryptData) {
        // 解密
        $info = $this->uInfo;
        $headers = $this->headers;
        if (isset($info['en_key']) && $info['en_key']) {
            $key = $info['en_key'];
        } else {
            if ($headers['os'] == 1) {
                $key = $this->ios_key;
            } else {
                $key = $this->and_key;
            }
        }

        $Dense = new Dense();
        $data = $Dense->decrypt($encryptData, $key);
        $this->params = json_decode($data, true);


    }

    /**
     * 加密
     * @param $data 数据
     */
    protected function encryptData($data, &$sourceKey) {
        $info = $this->uInfo;
        $headers = $this->headers;
        if (isset($info['en_key']) && $info['en_key']) {
            $sourceKey = 1;
            $key = $info['en_key'];
        } else {
            if ($headers['os'] == 1) {
                $key = $this->ios_key;
            } else {
                $key = $this->and_key;
            }
        }
        $Dense = new Dense();
        $data = $Dense->encrypt($data, $key);
        return array('datas' => $data);
    }

    /**
     * 签名
     * @param $data
     * @param $sourceKey
     * @return array
     */
    protected function makeSign($data, $sourceKey) {
        $time = time();
        $sign = md5($data . $time);
        return array('datas' => $data, 'timestamp' => $time, 'sign' => $sign, 'sourceKey' => $sourceKey);
    }

    /**
     * 参数验证
     * @param $fields
     * @return mixed
     * @throws Exception
     */
    protected function checkParams($fields) {
        if (!empty($fields)) {
            $fields = is_string($fields) ? explode(',', $fields) : $fields;
            foreach ($fields as $k) {
                if (!isset($this->params[$k]) || (empty($this->params[$k]) && $this->params[$k] !== 0 && $this->params[$k] !== '0')) {
                    $this->resErr($k . ' can not be empty', RespCode::PARAMS_ERR);
                }
            }
        }
        return $this->params;
    }

    /**
     * api接口错误返回
     * @param $msg
     * @param int $code
     * @param string $fields
     * @return false|string
     */
    protected function resErr($msg = '', $code = RespCode::DEFAULT_ERR, $fields = '*') {
        $is_arr_res = $this->isArrayResult;
        if (count($is_arr_res) > 0) {
            foreach ($is_arr_res as $key => $value) {
                $new_array[] = strtolower(trim($value));
            }
        } else {
            $new_array = [];
        }
        if (in_array(strtolower($this->request->action()), $new_array)) {
            $data = [];
        } else {
            $data = new \stdClass();
        }
        $code == 0 && $code = RespCode::DEFAULT_ERR;
        $time = time();
        $result = compact('code', 'msg', 'data', 'time', 'fields');
        if ($msg == '') $result = array_merge($result, $this->getErrInfo());
        $sourceKey = 0;
        if ($this->isEncrypt) {
            $result = $this->encryptData($result, $sourceKey);
        }
        if ($this->isSign) {
            $result = $this->makeSign($result['datas'], $sourceKey);
        }
        //重新处理特殊异常
        if (Env::get('app.dev') == 'online' && in_array($code, [10501])) $result['msg'] = '服务器异常';

        json($result)->send();
        exit;
    }

    /**
     * 错误数据处理
     * @return array
     */
    protected function getErrInfo() {
        $msgInfo = Err::getMessage();
        if (!isset($msgInfo[0])) {
            return [
                'msg'    => 'fail',
                'code'   => 400,
                'fields' => '*'
            ];
        }
        return $msgInfo[0];
    }

    /**
     * api接口正确返回
     * @param $msg
     * @param array $data
     * @param int $code
     * @return false|string
     */
    protected function resData($msg, $data, $code = RespCode::SUCCESS) {
        $is_arr_res = $this->isArrayResult;
        if (count($is_arr_res) > 0) {
            foreach ($is_arr_res as $key => $value) {
                $new_array[] = strtolower(trim($value));
            }
        } else {
            $new_array = [];
        }
        if (empty($data)) {
            if (in_array(strtolower($this->request->action()), $new_array)) {
                $data = [];
            } else {
                $data = new \stdClass();
            }
        }
        $time = time();
        $result = compact('code', 'msg', 'data', 'time');
        $sourceKey = 0;
        if ($this->isEncrypt) {
            $result = $this->encryptData($result, $sourceKey);
        }
        if ($this->isSign) {
            $result = $this->makeSign($result['datas'], $sourceKey);
        }
        if ($code == 200) {
            switch (strtolower(request()->module())) {
                case 'admin':
                    $systemLog = new \app\admin\model\SystemLog();
                    $systemLog->footPrint($this->uInfo);
                    break;
                case 'badmin':
                    $systemLog = new \app\badmin\model\SystemLog();
                    $systemLog->footPrint($this->uInfo);
                    break;
                default:
                    break;
            }
        }

        json($result)->send();
        exit;
    }

}